<?php
///////////////////////////////////////////////////////////////////////////////////////////////////////
//  这个文件是 JCAT PHP框架的一部，该项目和此文件 均遵循 GNU 自由软件协议
// 
//  Copyleft 2008 JeCat.cn(http://team.JeCat.cn)
//
//
//  JCAT PHP框架 的正式全名是：Jellicle Cat PHP Framework。
//  “Jellicle Cat”出自 Andrew Lloyd Webber的音乐剧《猫》（《Prologue:Jellicle Songs for Jellicle Cats》）。
//  JCAT 是一个开源项目，它像音乐剧中的猫一样自由，你可以毫无顾忌地使用JCAT PHP框架。JCAT 由中国团队开发维护。
//  正在使用的这个版本是：0.5.0 / SVN信息: $Id: class.JCAT_Model.php 1603 2009-03-31 15:35:42Z alee $
//
//
//
//  相关的链接：
//    [主页] http://jcat.JeCat.cn
//    [下载(HTTP)] http://code.google.com/p/jcat-php/downloads/list
//    [下载(svn)] svn checkout http://jcat-php.googlecode.com/svn/branches/0.4.0/Framework/ JCAT0.4
//    [在线文档] http://jcat.JeCat.cn/document
//    [社区] http://jj.jecat.cn/forum-7-1.html
//  不很相关：
//    [MP3] http://www.google.com/search?q=jellicle+songs+for+jellicle+cats+Andrew+Lloyd+Webber
//    [VCD/DVD] http://www.google.com/search?q=CAT+Andrew+Lloyd+Webber+video
//
///////////////////////////////////////////////////////////////////////////////////////////////////////
/*-- Project Introduce --*/
namespace JCAT ;




/**
 * What's this Class ?
 *
 * @author		alee
 * @access		public
 */
abstract class Model extends EventProcessor implements IModel
{

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	void
	 */
	public function Model()
	{
		$this->_CommonConstruct() ;
		$this->aModelContainer = new MVCObjectContainer('IModel',true) ;
	}

	/**
	 * 常规构造函数， 在 构造函数 和  __wakeup() 中 调用
	 * 
	 * @access	protected
	 * @return	void
	 */
	protected function _CommonConstruct()
	{
		$this->aViewContainer = new MVCObjectContainer('IView') ;
	}

	/**
	 * 序列化
	 * 
	 * @access	public
	 * @return	void
	 */
	public function Serialize()
	{
		if( !$this->IsChanged() )
		{
			return true ;			
		}
		
		// 保存
		if( $this->HaveSerialized() )
		{
			return $this->Save() ;
		}
		
		// 新建
		else
		{
			return $this->Create() ;
		}
	}


	/**
	 * restore a model
	 * 
	 * @access	public
	 * @return	bool
	 */
	public function Unserialize()
	{
		// 读取操作 
		if( $this->Load() )
		{
			// 设置变化
			$this->SetChanged(false) ;
			return true ;
		}

		else 
		{
			return false ;
		}
	}

	
	/**
	 * return if the model have been serialized
	 *
	 * @access	public
	 * @return	bool
	 */
	public function HaveSerialized()
	{
		return $this->bHaveBeenSerialized ;
	}

	/**
	 * Description
	 *
	 * @access	public
	 * @param	$bSerialized=true		bool
	 * @return	void
	 */
	public function SetSerialized($bSerialized=true)
	{
		$this->bHaveBeenSerialized = $bSerialized? true: false ;
	}
	
	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$sKey	string	What's this Parameter ?
	 * @return	void
	 */
	public function __get( $sKey ) 
	{
		if( isset($this->arrDatas[$sKey]) )
		{
			return $this->arrDatas[$sKey] ;
		}
		
		else
		{
			throw new Exception( Language::SentenceEx('正在访问未知的对象属性：“%s::%s”','JCAT',null, get_class($this), $sKey), __macro_exception_code__ ) ;
		}
	}

	public function __sleep()
	{
		$arrSavePropertys[] = Package::MakePrivatePropertyNameForSerialize('arrDatas',__CLASS__) ;
		$arrSavePropertys[] = Package::MakePropertyPropertyNameForSerialize('aModelContainer') ;
		return $arrSavePropertys ;
	}

	public function __wakeup()
	{
		$this->_CommonConstruct() ;
	}
	
	function __clone()
    {
        $this->aModelContainer = clone $this->aModelContainer ;
        $this->aViewContainer = clone $this->aViewContainer ;
    }
	
	/**
	 * __call
	 *
	 * @access	public
	 * @param	$sCallName
	 * @param	$arrArguments=array()	array
	 * @return	void
	 */
	public function __call($sCallName,array $arrArguments=array())
	{
		//  Alias for Set/Get() methods
		// -----------------------------------------------------------------------------
		if( $this->IsEnableSetDataMethod() )
		{
			$sSetOrGet = strtolower(substr($sCallName,0,3)) ;
			
			// alias for self::Get('aaa')
			if( $sSetOrGet=='get' )
			{
				return $this->Get(substr($sCallName,3)) ;
			}
			
			// alias for self::Set('aaa','xxx')
			else if ( $sSetOrGet=='set' )
			{
				array_unshift($arrArguments,substr($sCallName,3)) ;
				return call_user_func_array(array($this,$sSetOrGet),$arrArguments) ;
			}
		}
		
		//  error spell
		// -----------------------------------------------------------------------------
		throw new Exception(Language::SentenceEx(
			'正在访问不存在的方法 %s::%s()'
			, 'JCAT', null
			, get_class($this), $sCallName
		)) ;
	}
    
	/**
	 * 检查模型数据 $sDataName 是否存在 
	 * 
	 * @access	public
	 * @param	$sDataName		string
	 * @return	bool
	 */
	public function IsDataExists($sDataName)
	{
		$arrDatas =& $this->arrDatas ;		// array_key_exists() 无法直接访问私有属性 $this->arrDatas 
		return array_key_exists($sDataName,$arrDatas) ;
	}


	///////// 实现 IModel 接口 ///////////////////////////////////////////////////////////////////////////////


	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$sDataName	string	What's this Parameter ?
	 * @return	mixed, null
	 */
	public function Get( $sDataName )
	{
		ASSERT::ASSERT_STRING($sDataName) ;
		return isset($this->arrDatas[$sDataName])? $this->arrDatas[$sDataName]: null ;
	}



	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$sDataName				string	What's this Parameter ?
	 * @param	$Value					mixed		What's this Parameter ?
	 * @param	$bSetChanged=true		bool		What's this Parameter ?
	 * @return	old_var
	 */
	public function Set( $sDataName, $Value, $bSetChanged=true )
	{
		ASSERT::ASSERT_STRING($sDataName) ;

		if( !$this->IsDataExists($sDataName) and $this->IsStructLocking() )
		{
			throw new Exception(Language::SentenceEx(
				'正在存储一个不存在的模型数据：“%s”。当前模型（%s）的结构已经处于锁定状态，无法动态改变模型结构。'
				, 'JCAT', null
				, $sDataName,$this->GetName()
			)) ;
		}

		$old_var = isset($this->arrDatas[$sDataName])? $this->arrDatas[$sDataName]: null ;
		$this->arrDatas[$sDataName] = $Value ;
		
		if( $bSetChanged and $Value!==$old_var )
		{
			$this->SetChanged() ;
		}
			
		return $old_var ;
	}

	/**
	 * lock the model struct
	 *
	 * @access	public
	 * @return	void
	 */
	public function LockStruct()
	{
		$this->bStructLocking = true ;
	}

	/**
	 * unlock the model struct
	 *
	 * @access	public
	 * @return	void
	 */
	public function UnlockStruct()
	{
		$this->bStructLocking = false ;
	}

	/**
	 * the model struct is locking
	 *
	 * @access	public
	 * @return	bool
	 */
	public function IsStructLocking()
	{
		return $this->bStructLocking ;	
	}

	/**
	 * 返回所有数据 的 数组，仅提供给派生类
	 * 
	 * @access	protected
	 * @param 	$bExcludeNull=false	bool	排除为 null 的数据
	 * @return	array
	 */
	protected function GetDatas( $bExcludeNull=false )
	{
		if($bExcludeNull)
		{
			$arrRet = array() ;
			
			foreach ($this->arrDatas as $sDataName=>&$Data)
			{
				if($Data!==self::NULL_DATA_VALUE)
				{
					$arrRet[$sDataName] = $Data ;
				}
			}
			
			return $arrRet ;
		}
		
		else 
		{
			return $this->arrDatas ;
		}
	}

	/**
	 * 一次性设置所有数据，仅提供给派生类
	 * 
	 * @access	protected
	 * @param 	$arrDatas	array  数据数组
	 * @return	old_value
	 */
	protected function SetDatas( array $arrDatas )
	{
		$old_value = $this->arrDatas ;
		$this->arrDatas =& $arrDatas ;
		return $old_value ;
	}



	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	bool
	 */
	public function IsChanged() 
	{		
		return $this->bChanged ;
	}



	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$bChanged=true			bool	What's this Parameter ?
	 * @param	$bSetChildren=false		bool	What's this Parameter ?
	 * @return	old_var
	 */
	public function SetChanged( $bChanged=true, $bSetChildren=false )
	{
		$old_var = $this->IsChanged($bSetChildren) ;
		
		// 更新 自身
		$this->bChanged = (bool)$bChanged ;
		
		// 更新 子模型
		if( $bSetChildren )
		{
			$aIterator = $this->CreateModelIterator() ;
			$aIterator->First() ;
			while( !$aIterator->IsDone() )
			{
				$aChild = $aIterator->Current() ;
				
				$aChild->SetChanged($bChanged,$bSetChildren) ;
				
				$aIterator->Next() ;
			}
			
		}
		
		
		return $old_var ;
	}



	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$aBut=null	IView	What's this Parameter ?
	 * @return	void
	 */
	public function UpdateAllView( IView $aBut=null )
	{
		$aIterator = $this->CreateViewIterator() ;
		$aIterator->First() ;
		while( !$aIterator->IsDone() )
		{
			$aView = $aIterator->Current() ;
			
			if($aView!==$aBut)
			{
				// !! 因为 视图及其 子视图 可能 设置为相同的模型，
				// 模型负责 更新所有的 视图，
				// 视图 更新时 更新其子视图，则会造成 子视图的 重复更新。
				$aView->Update(false) ;
			}
			
			$aIterator->Next() ;
		}
	}



	/**
	 * 彻底 销毁一个模型 在  MVC 系统中的所有引用，以便在不需要该模型时能够立刻回收：
	 *  模型的全局名称上的引用
	 *  ... ...
	 * 
	 * @access	public
	 * @return	void
	 */
	public function Destroy()
	{
		// 清除 视图的全局名称
		self::UnregisterGlobalName($this) ;
	}
	
	///////// 实现 IModelContainer 接口 ///////////////////////////////////////////////////////////////////////////////
	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$aModel		IModel	What's this Parameter ?
	 * @param	$sName=null		string			What's this Parameter ?
	 * @return	void
	 */
	public function AddModel( IModel $aModel, $sName=null )
	{
		$this->SetChanged(true) ;
		return $this->aModelContainer->Add($aModel,$sName) ;
	}

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$aModel		IModel	What's this Parameter ?
	 * @return	void
	 */
	public function RemoveModel( IModel $aModel )
	{
		$this->SetChanged(
			($this->aModelContainer->Remove($aModel)? true: false)
		) ;
	}

	/**
	 * 取得一个子模型
	 * 
	 * @access	public
	 * @param	$sName	string	What's this Parameter ?
	 * @return	Model
	 */
	public function GetModel( $sName )
	{
		ASSERT::ASSERT_NOTNULL($this->aModelContainer
				, Language::SentenceEx('缺少 %s::$aModelContainer ，模型可能没有调用父类构造函数完成初始化。','JCAT',null,__CLASS__)) ;
		return $this->aModelContainer->Get($sName) ;
	}

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	int
	 */
	public function GetModelCount(  ) 
	{ return $this->aModelContainer->GetObjectCount() ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	int
	 */
	public function ClearModel(  )
	{ return $this->aModelContainer->ClearObject() ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	ArrayIterator
	 */
	public function CreateModelIterator(  )
	{ return $this->aModelContainer->CreateObjectIterator() ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	array
	 */
	public function CreateModelMemento(  )
	{ return $this->aModelContainer->CreateObjectMemento() ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$arrMemento	array	What's this Parameter ?
	 * @return	void
	 */
	public function SetModelMemento( $arrMemento )
	{
		ASSERT::ASSERT_ISTHESE($arrMemento,array('array:JCAT\IModel')) ;
		return $this->aModelContainer->SetObjectMemento($arrMemento) ;
	}

	
	
	
	///////// 实现 IViewContainer 接口 ///////////////////////////////////////////////////////////////////////////////
	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$aView		IView		What's this Parameter ?
	 * @param	$sName=null	string			What's this Parameter ?
	 * @return	void
	 */
	public function AddView( IView $aView, $sName=null )
	{
		return $this->aViewContainer->Add($aView,$sName) ;
	}
	

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$View		IView,string		What's this Parameter ?
	 * @return	void
	 */
	public function RemoveView( $View )
	{ return $this->aViewContainer->Remove($View) ; }


	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$sName	string	What's this Parameter ?
	 * @return	
	 */
	public function GetView( $sName )
	{ return $this->aViewContainer->Get($aView) ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	int
	 */
	public function GetViewCount(  ) 
	{ return $this->aViewContainer->GetObjectCount() ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	int
	 */
	public function ClearView(  )
	{ return $this->aViewContainer->ClearObject() ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	ArrayIterator
	 */
	public function CreateViewIterator(  )
	{ return $this->aViewContainer->CreateObjectIterator() ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	array
	 */
	public function CreateViewMemento(  )
	{ return $this->aViewContainer->CreateObjectMemento() ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$arrMemento	array	What's this Parameter ?
	 * @return	void
	 */
	public function SetViewMemento( $arrMemento )
	{ return $this->aViewContainer->SetObjectMemento($arrMemento) ; }


	/**
	 * 设置模型是否可通过方法 SetXX() 来设置 'XX'
	 *
	 * @access	public
	 * @param	$bEnable		bool	PropertyDescription
	 * @return	void
	 */
	public function EnableSetDataMethod($bEnable)
	{
		$this->bEnableSetDataMethod = $bEnable? true: false ;
	}

	/**
	 * 返回模型是否可通过方法 SetXX() 来设置 'XX'
	 *
	 * @access	public
	 * @return	bool
	 */
	public function IsEnableSetDataMethod()
	{
		return $this->bEnableSetDataMethod ;
	}


	// 属性 ///////////////////////////////////////////////////////////////////////////////

	/**
	 * the model struct is locking
	 * 
	 * @access	private
	 * @var		bool
	 */
	private $bStructLocking = false ;

	/**
	 * What's this Attribute ?
	 * 
	 * @access	private
	 * @var		array
	 */
	private $arrDatas = array() ;
	
	/**
	 * What's this Attribute ?
	 * 
	 * @access	protected
	 * @var		MVCObjectContainer
	 */
	protected $aModelContainer ;

	/**
	 * What's this Attribute ?
	 * 
	 * @access	private
	 * @var		MVCObjectContainer
	 */
	private $aViewContainer ;

	/**
	 * What's this Attribute ?
	 * 
	 * @access	private
	 * @var		bool
	 */
	private $bChanged = false ;
	
	/**
	 * Description
	 * 
	 * @access	private
	 * @var		bool
	 */
	private $bHaveBeenSerialized = false ;

	/**
	 * Description
	 * 
	 * @access	private
	 * @var		bool
	 */
	private $bEnableSetDataMethod = false ;
	
	
	const NULL_DATA_VALUE = null ; 
}


?>
